#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h> 
using namespace std;

#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]久 "

// 全局数据
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc = 0;

// 环境变量
#define MAX_ENV 1024
char *g_env[MAX_ENV];
int envNum = 0;

// for test 
char cwd[1024];
char cwdenv[1024];

// last exit code
int lastcode = 0;

// last pwd 
char lastPwd[1024];

// 重定向
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3

int redir = NONE_REDIR;
string filename;

void printEnv()
{
  for(int i = 0; g_env[i]; i++)
  {
    printf("%s\n", g_env[i]);
  }
}

//void ChPWD()
//{
//  for(int i = 0; g_env[i]; i++)
//  {
//    string e = g_env[i];
//    int pos = e.find("=");
//    if(e.substr(0, pos) == "PWD")
//    {
//      strcpy(g_env[i], cwdenv);
//      g_env[i][strlen(cwdenv)] = '\0';
//      //printEnv();
//      return;
//    }
//  }
//}

const char *GetUserName()
{
  const char *name = getenv("USER");
  return name == NULL ? "None" : name;
}

const char *GetHostName()
{
  const char *hostname = getenv("HOSTNAME");
  return hostname == NULL ? "None" : hostname;
}

const char *GetPwd()
{
  //const char *pwd = getenv("PWD"); //cd时不会改变环境变量
  const char *pwd = getcwd(cwd, sizeof(cwd));
  if(pwd != NULL)
  {
    snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", pwd);
    //ChPWD();
    putenv(cwdenv);
  }
  return pwd == NULL ? "None" : pwd;
}


const char *GetHome()
{
  const char *home = getenv("HOME");
  return home == NULL ? NULL:home;
}

string DirName(const char *pwd)
{
#define SLASH "/"
  string dir = pwd;
  if(dir == SLASH) return SLASH;
  auto pos = dir.rfind(SLASH);
  if(pos == string::npos) return "BUG?";
  return dir.substr(pos+1);
}

// 生成命令行提示符
void MakeCommandLine(char cmd_prompt[], int size) 
{
  snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str()); 
}

// 打印命令行提示符
void PrintCommandPrompt()
{
  char prompt[COMMAND_SIZE];
  MakeCommandLine(prompt, sizeof(prompt));
  printf("%s", prompt);
  fflush(stdout);
}

// 获取用户命令
bool GetCommandLine(char *out, int size)
{
 // char *c = fgets(out, size, stdin);
 // if(c == NULL) return false;
 // out[strlen(out)-1] = 0;
 // if(strlen(out) == 0) return false;
 // return true;
 
  char prompt[COMMAND_SIZE];
  MakeCommandLine(prompt, sizeof(prompt));

  char *line = readline(prompt); // 读取输入(支持退格键), 在调用时会将命令行提示符显示出来
  if (!line) return false;
  strncpy(out, line, size - 1);
  out[size - 1] = '\0';
  if (strlen(out)) add_history(line); // 可选添加到readline历史记录
  free(line);
  return true;
}

// 分析命令
bool CommandParse(char *commandline)
{
#define SEP " "
  g_argc = 0;
  g_argv[g_argc++] = strtok(commandline, SEP);
  //最后一个为NULL
  while((bool)(g_argv[g_argc++] = strtok(nullptr, SEP)));
  --g_argc;
  return g_argc > 0 ? true:false;
}

// 打印命令行参数
void PrintArgv()
{
  for(int i = 0; g_argv[i]; i++)
  {
    printf("argv[%d]=%s\n", i, g_argv[i]);
  }
  printf("argc=%d\n", g_argc);
}

// 执行命令
void Execute()
{
  pid_t id = fork();
  if(id == 0)
  {
    int fd = -1;
    //子进程 检测重定向情况
    if(redir == INPUT_REDIR)
    {
      fd = open(filename.c_str(), O_RDONLY);
      if(fd < 0) exit(1);
      dup2(fd, 0);
      close(fd);
    }
    else if(redir == OUTPUT_REDIR)
    {
      fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
      if(fd < 0) exit(1);
      dup2(fd, 1);
      close(fd);
    }
    else if(redir == APPEND_REDIR)
    {
      fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
      if(fd < 0) exit(1);
      dup2(fd, 1);
      close(fd);
    }
    // 进程替换后不影响重定向结果
    execvp(g_argv[0], g_argv);
    exit(1);
  }
  int status;
  pid_t rid = waitpid(id, &status, 0);
  if(rid > 0)
  {
    lastcode = WEXITSTATUS(status);
  }
}

void InitEnv()
{
  extern char **environ;
  memset(g_env, 0, sizeof(g_env));
  envNum = 0;

  // 获取环境变量
  for(int i = 0; environ[i]; i++)
  {
    g_env[i] = (char*)malloc(strlen(environ[i]) + 1);
    strcpy(g_env[i], environ[i]);
    ++envNum;
  }

  // for test
  //char *testEnv = (char*)"HAHA=for_test";
  //g_env[envNum] = (char*)malloc(strlen(testEnv) + 1);
  //strcpy(g_env[envNum++], testEnv);

  g_env[envNum] = NULL;

  // 导成环境变量
  for(int i = 0; g_env[i]; i++)
  {
    putenv(g_env[i]);
    //将环境变量导入到当前进程的环境表中,其子进程也可以拿到
  }
}

void Cd()
{
  char Pwd[1024];
  strcpy(Pwd, lastPwd);
  getcwd(lastPwd, sizeof(lastPwd));
  if(g_argc == 1)
  {
    string home = GetHome();
    if(home.empty()) return;
    chdir(home.c_str());
  }
  else 
  {
    string where = g_argv[1];
    if(where == "-")
    {
      chdir(Pwd);
    }
    else if(where == "~")
    { 
      string home = GetHome();
      if(home.empty()) return;
      chdir(home.c_str());
    }
    else 
    {
      chdir(where.c_str());
    }
  }
}

void Echo()
{
  if(g_argc == 2)
  {
    string opt = g_argv[1];
    if(opt == "$?")
    {
      cout << lastcode << endl;
      lastcode = 0;
    }
    else if(opt[0] == '$')
    {
      string envName = opt.substr(1);
      const char *envValue = getenv(envName.c_str());
      cout << envValue << endl;
    }
    else 
    {
      // 打印不存在空格的字符串
      //cout << opt << opt.size();
      string str = opt.substr(1, opt.size()-2);
      cout << str << endl;
    }
  }
  // 打印存在空格的字符串
  else if(g_argc > 2)
  {
    string str = g_argv[1];
    cout << str.substr(1) << " ";
    for(int i = 2; i < g_argc - 1; i++)
    {
      cout << g_argv[i] << " ";
    }
    string strr = g_argv[g_argc - 1];
    cout << strr.substr(0, strr.size()-1) << endl;
  }
}

// 执行内建命令
bool CheckAndExecuteBuiltin()
{
  string cmd = g_argv[0];
  if(cmd == "cd")
  {
    Cd();
    return true;
  }
  else if(cmd == "echo")
  {
    Echo();
    return true;
  }
  //else if(cmd == "Env")
  //{
  //  printEnv();
  //  return true;
  //}
  return false;
}

// 清理环境变量
void cleanup()
{
  for(int i = 0; g_env[i]; i++)
  {
    free(g_env[i]);
  }
  //free(g_env); //g_env不是malloc的
}

void TrimSpace(char cmd[], int &index)
{
  while(isspace(cmd[index]))
  {
    index++;
  }
}

void RedirCheck(char cmd[])
{
  redir = NONE_REDIR;
  filename.clear();
  int start = 0;
  int end = strlen(cmd) - 1;

  while(end > start)
  {
    if(cmd[end] == '<')
    {
      cmd[end++] = 0;
      TrimSpace(cmd, end);
      redir = INPUT_REDIR;
      filename = cmd+end;
      break;
    }
    else if(cmd[end] == '>')
    {
      if(cmd[end-1] == '>')
      {
        cmd[end-1] = 0;
        end++;
        TrimSpace(cmd, end);
        redir = APPEND_REDIR;
        filename = cmd+end;
        break;
      }
      else
      {
        cmd[end++] = 0;
        TrimSpace(cmd, end);
        redir = OUTPUT_REDIR;
        filename = cmd+end;
        break;
      }
    }
    else 
    {
      end--;
    }
  }
    
}

int main()
{
  getcwd(lastPwd, sizeof(lastPwd));
  // shell 启动时从系统中获取环境变量
  InitEnv();
  
  while(true)
  {
    //1. 输出命令行提示符
    //PrintCommandPrompt();

    //2. 获取用户命令
    char commandline[COMMAND_SIZE];
    if(!GetCommandLine(commandline, sizeof(commandline)))
      continue;
   
    //3. 重定向分析
    RedirCheck(commandline);


    //4. 命令行分析
    if(!CommandParse(commandline))
      continue;
    //PrintArgv();

    //5. 检测并处理内建命令
    if(CheckAndExecuteBuiltin())
      continue;

    //6. 执行命令
    Execute();
  }
  
  cleanup();
  return 0;
}
